package com.maikang.exammall.service;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.common.collect.Sets;
import com.maikang.exammall.entity.Commission;
import com.maikang.exammall.entity.Sale;
import com.maikang.exammall.entity.User;
import com.maikang.exammall.entity.UserContext;
import com.maikang.exammall.entity.constant.OrderStatus;
import com.maikang.exammall.entity.dto.promote.Client;
import com.maikang.exammall.entity.dto.promote.EarningDTO;
import com.maikang.exammall.entity.dto.promote.EarningDetailDTO;
import com.maikang.exammall.entity.dto.user.LoginInputDTO;
import com.maikang.exammall.entity.dto.user.UserProfileDTO;
import com.maikang.exammall.exception.BadRequestException;
import com.maikang.exammall.exception.ForbiddenException;
import com.maikang.exammall.repository.CommissionRepository;
import com.maikang.exammall.repository.DictRepository;
import com.maikang.exammall.repository.SaleRepository;
import com.maikang.exammall.repository.UserRepository;
import com.maikang.exammall.util.*;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.SliceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.Set;

import static com.maikang.exammall.entity.constant.OrderStatus.COMMENTED;
import static com.maikang.exammall.entity.constant.OrderStatus.EXAMINED_TO_COMMENT;
import static com.maikang.exammall.entity.constant.RedisKeyConstant.*;
import static com.maikang.exammall.util.BeanUtils.getNullPropertyNames;
import static com.maikang.exammall.util.CosUtil.createUserCaptchaStorePathFromUserId;

@Log4j2
@Service
@RequiredArgsConstructor
public class UserService extends BaseService<User, Long, UserRepository> {
    private final WxMpService wxService;
    private final CommissionService commissionService;
    private final CommissionRepository commissionRepository;
    private final UserRepository userRepository;
    private final CosUtil cosUtil;
    private final TokenUtil tokenUtil;
    private final ImageUtil imageUtil;
    private final DefaultKaptcha defaultKaptcha;
    private final RedisService redisService;
    private final DictRepository dictRepository;
    private final SmsUtil smsUtil;
    private final SaleRepository saleRepository;
    private static final Set<String> suffixes = Sets.newHashSet("image/png", "image/jpeg");

    private static final String folder = "recruit_qrcode/";

    @SneakyThrows
    public String recruitQrcode() {
        String recruitBackgroundImageUrl = dictRepository.findByKey("recruitBackgroundImageUrl").getValue();
        Long userId = UserContext.getUserId();
        User user = userRepository.findById(userId).get();
//        if (StringUtils.hasLength(user.getRecruitPicUrl())) {
//            return user.getRecruitPicUrl();
//        }

        if (!StringUtils.hasLength(user.getRecruitQrcodePicUrl())) {
            WxMpQrCodeTicket ticket = wxService.getQrcodeService().qrCodeCreateLastTicket(user.getId().intValue());
            File file = wxService.getQrcodeService().qrCodePicture(ticket);
            String qrcodeUrl = cosUtil.upload(folder + "raw_qrcode_" + user.getId() + ".jpg", file);
            log.info("created qrcodeUrl for user {} is {}", userId, qrcodeUrl);
            user.setRecruitQrcodePicUrl(qrcodeUrl);
        }

        String qrCodeImage = imageUtil.createQrCodeImage(recruitBackgroundImageUrl, user.getAvatarUrl(), user.getUsername(), user.getRecruitQrcodePicUrl(), folder + "composed_recruit_pic_" + user.getId() + ".jpg");
        log.info("created composed qrcodeUrl for user {} is {}", userId, qrCodeImage);
        user.setRecruitPicUrl(qrCodeImage);

        User save = userRepository.save(user);
        return qrCodeImage;
    }

    @Transactional(rollbackFor = Exception.class)
    public UserProfileDTO saveUserProfile(UserProfileDTO entity) {
        User target = repository.findById(UserContext.getUserId()).get();
        BeanUtils.copyProperties(entity, target, getNullPropertyNames(entity));
        User save = repository.save(target);
        BeanUtils.copyProperties(save, entity);
        return entity;
    }

    @SneakyThrows
    public UserProfileDTO uploadAvatar(MultipartFile multipartFile) {
        validateUploadingImage(multipartFile);
        String path = CosUtil.createUserAvatarStorePathFromMultipart(multipartFile, UserContext.getUserId());
        String avatarUrl = cosUtil.upload(path, multipartFile);
        User u = repository.findById(UserContext.getUserId()).get();
        u.setAvatarUrl(avatarUrl);
        User save = repository.save(u);
        return new UserProfileDTO(save);
    }

    private void validateUploadingImage(MultipartFile multipartFile) throws IOException {
        User user = UserContext.getUser();
        String contentType = multipartFile.getContentType();
        String originalFilename = multipartFile.getOriginalFilename();
        if (!suffixes.contains(contentType)) {
            throw new BadRequestException("上传失败, 不支持上传该格式的图片");
        }
        BufferedImage bufferedImage = ImageIO.read(multipartFile.getInputStream());
        if (bufferedImage == null) {
            throw new BadRequestException("上传失败, 文件内容不符合要求");
        }
        log.info("用户{} 上传图片:name={},type={}", user, originalFilename, contentType);
    }

    public UserProfileDTO findUserProfile() {
        Long userId = UserContext.getUserId();
        Sale sale = saleRepository.findBySaleId(userId);
        UserProfileDTO profile = repository.findUserProfileById(userId);
        EarningDTO earning;
        if (sale != null) {
            profile.setRole(sale.getRole());
            earning = commissionService.findEarningBySaleId(userId);
        } else {
            profile.setRole(0);
            earning = new EarningDTO(0L,0L,0L,0L);
        }
        profile.setEarning(earning);
        return profile;
    }



    public String createCaptchaCode() {
        String text = defaultKaptcha.createText();
        BufferedImage image = defaultKaptcha.createImage(text);
        // save text code to redis
        redisService.set(getUserCaptchaKeyFromUserId(UserContext.getUserId()), text, 5 * 60);
        // upload image to cos
        String path = createUserCaptchaStorePathFromUserId(UserContext.getUserId());
        return cosUtil.upload(path, image);
    }

    public void createSMSCode(String mobile) {
        String isSMSSentMomentAgoKey = getIsSMSSentMomentAgoKeyFromUserId(UserContext.getUserId());
        if (redisService.get(isSMSSentMomentAgoKey) != null) {
            throw new BadRequestException("请求发送短信频繁，请稍后再试");
        }
        String smsCode = IDUtil.create6RandomDigit();
        redisService.set(getUserLoginSMSKeyFromMobile(mobile), smsCode, 5 * 60);
        redisService.set(isSMSSentMomentAgoKey, true, 60);
        smsUtil.sendRegisterSMS(smsCode, "+86" + mobile);
        log.info("login smsCode {} is sent to mobile {} by userId {}", smsCode, mobile, UserContext.getUserId());
    }

    @Transactional(rollbackFor = Exception.class)
    public UserProfileDTO login(LoginInputDTO inputDTO) {
        Long userId = UserContext.getUserId();
        String userCaptchaKey = getUserCaptchaKeyFromUserId(userId);
        String userLoginSMSKey = getUserLoginSMSKeyFromMobile(inputDTO.getMobile());
        if (!checkCode(userCaptchaKey, inputDTO.getCaptchaCode())) {
            throw new BadRequestException("图片验证码有误");
        }
        if (!checkCode(userLoginSMSKey, inputDTO.getSmsCode())) {
            throw new BadRequestException("短信验证码有误");
        }
        User user = repository.findById(userId).get();
        user.setMobile(inputDTO.getMobile());
        User save = repository.save(user);
        return new UserProfileDTO(save);
    }

    private boolean checkCode(String key, String inputCode) {
        Object codeInRedis = redisService.get(key);
        return codeInRedis != null && codeInRedis.equals(inputCode);
    }

    public SliceImpl<EarningDetailDTO> findEarning(Date fromDate, Date toDate, Pageable pageable) {
        Long userId = UserContext.getUserId();
        if (!saleRepository.existsBySaleId(userId)) {
            throw new ForbiddenException();
        }
        return commissionService.findCommissionList(userId, fromDate, toDate, pageable);
    }

    @Transactional(readOnly = true)
    public Iterable<Client> findClients(Boolean isEffective, Pageable pageable) {
        Long userId = UserContext.getUserId();
        if (!saleRepository.existsBySaleId(userId)) {
            throw new ForbiddenException();
        }
        if (isEffective != null && isEffective) {
            return repository.findByRefererIdAndOrderStatus(userId, EXAMINED_TO_COMMENT, COMMENTED, pageable);
        }
        return repository.findByRefererIdOrderByCreatedDateDesc(userId, pageable);
    }

    @Transactional(rollbackFor = Exception.class)
    public Commission withdraw(Commission commission) {
        Long userId = UserContext.getUserId();
        if (!saleRepository.existsBySaleId(userId)) {
            throw new ForbiddenException();
        }
        EarningDTO earning = commissionService.findEarningBySaleId(userId);
        if(commission == null || commission.getAmount() > earning.getWithdrawable()) {
            throw new BadRequestException("当前可提现余额不足");
        }

        Commission build = Commission.builder().outTradeNo(IDUtil.createSaleWithdrawOutTradeNo())
                .saleId(userId)
                .status(OrderStatus.WITHDRAWING)
                .amount(commission.getAmount()).build();

        return commissionRepository.save(build);


    }

}
